#!/usr/bin/env python3
# Copyright 2020 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
"""Decodes and detokenizes Base64-encoded strings in serial output.

The output is printed or saved to a file. Input is not supported.
"""

import argparse
import sys
from typing import BinaryIO, Iterable

import serial
from pw_tokenizer import database, detokenize, tokens


def _parse_args():
    """Parses and return command line arguments."""

    parser = argparse.ArgumentParser(
        description=__doc__,
        formatter_class=argparse.RawDescriptionHelpFormatter,
        parents=[database.token_databases_parser()],
    )
    parser.add_argument(
        '-d',
        '--device',
        required=True,
        help='The serial device from which to read',
    )
    parser.add_argument(
        '-b',
        '--baudrate',
        type=int,
        default=115200,
        help='The baud rate for the serial device',
    )
    parser.add_argument(
        '-o',
        '--output',
        type=argparse.FileType('wb'),
        default=sys.stdout.buffer,
        help=(
            'The file to which to write the output; '
            'provide - or omit for stdout.'
        ),
    )
    parser.add_argument(
        '-p',
        '--prefix',
        default=detokenize.NESTED_TOKEN_PREFIX,
        help=(
            'The one-character prefix that signals the start of a '
            'Base64-encoded message. (default: $)'
        ),
    )
    parser.add_argument(
        '-s',
        '--show_errors',
        action='store_true',
        help=(
            'Show error messages instead of conversion specifiers when '
            'arguments cannot be decoded.'
        ),
    )

    return parser.parse_args()


def _detokenize_serial(
    databases: Iterable,
    device: str,
    baudrate: int,
    show_errors: bool,
    output: BinaryIO,
    prefix: str,
) -> None:
    if output is sys.stdout:
        output = sys.stdout.buffer

    detokenizer = detokenize.Detokenizer(
        tokens.Database.merged(*databases), show_errors=show_errors
    )
    serial_device = serial.Serial(port=device, baudrate=baudrate)

    try:
        detokenizer.detokenize_base64_live(serial_device, output, prefix)
    except KeyboardInterrupt:
        output.flush()


def main():
    _detokenize_serial(**vars(_parse_args()))
    return 0


if __name__ == '__main__':
    sys.exit(main())
